#    This is part of WeatherFront.
#
#    Copyright (C) 2010 Stefan Sieber
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License along
#    with this program; if not, write to the Free Software Foundation, Inc.,
#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#

import objc
import Foundation
import AppKit
import time

import PyFR.Utilities

import urllib2
import re
import string



verbose=1
def log(s,level=1):
	if verbose >= level:
		Foundation.NSLog( u"%s: WeatherAPI: %@", "WeatherFrontLog", s ) 
	pass
	

"""
	Thanks to Fredrik Lundh (http://effbot.org). The XML parser sample on his website is the foundation for the XMLscanner class.
"""
class XMLscanner:
	
	def __init__(self):
		self.xml_stream = []
		# xml tokenizer pattern
		self.regex = re.compile("<([/?!]?\w+)|&(#?\w+);|([^<>&'\"=\s]+)|(\s+)|(.)")
		self.xml_entities = {"amp": "&", "apos": "'", "gt": ">", "lt": "<", "quot": '"'}

	def xml(self, attrib):
		#print "XML", attrib
		#self.xml_stream[XML] = attrib
		return
		
	def start(self, tag):
		#print "START", tag
		value = {}
		value[tag] = "START"
		self.xml_stream.append(value)
		return
		
	def end(self, tag):
		#print "END", tag
		value = {}
		value[tag] = "END"
		self.xml_stream.append(value)
		return
		
	def data(self, text):
		#print "DATA", repr(text)
		value = {}
		value["DATA"] = text
		self.xml_stream.append(value)
		return
		
	def scan(self, str):
		# split string into xml elements

		# create a scanner function for this string
		def gettoken(space=0, scan=self.regex.scanner(str).match):
			# scanner function (bound to the string)
			try:
				while 1:
					m = scan()
					code = m.lastindex
					text = m.group(m.lastindex)
					if not space or code != 4:
						return code, text
			except AttributeError:
				raise EOFError

		# token categories
		TAG = 1; ENTITY = 2; STRING = 3; WHITESPACE = 4; SEPARATOR = 5

		start = self.start; end = self.end; data = self.data;
		
		try:
			while 1:
				code, text = gettoken();
				if code == TAG:
					# deal with tags
					type = text[:1]
					if type == "/":
						# end tag
						end(text[1:])
						code, text = gettoken(1)
						if text != ">":
							raise SyntaxError, "malformed end tag"
					elif type == "!":
						# document type declaration (incomplete)
						value = []
						while 1:
							# parse start tag contents
							code, text = gettoken(1)
							if text == ">":
								break
							value.append(text)
						value = string.join(value, "")
					else:
						# start tag or procesing instruction
						tag = text
						attrib = {}
						while 1:
							# parse start tag contents
							code, text = gettoken(1);
							if text == ">":
								start(tag)
								if attrib != {}:
									data(attrib)
								break
							if text == "/":
								start(tag)
								data(attrib)
								end(tag)
								data(attrib)
								break
							if text == "?":
								if type != text:
									raise SyntaxError, "unexpected quotation mark"
								code, text = gettoken(1)
								if text != ">":
									raise SyntaxError, "expected end tag"
								try:
									self.xml(attrib)
								except AttributeError:
									pass
								break
							if code == STRING:
								# tag attribute
								key = text
								code, text = gettoken(1)
								if text != "=":
									raise SyntaxError, "expected equal sign"
								code, quote = gettoken(1)
								if quote != "'" and quote != '"':
									raise SyntaxError, "expected quote"
								value = []
								while 1:
									code, text = gettoken()
									if text == quote:
										break
									if code == ENTITY:
										try:
											text = fixentity(text)
										except ValueError:
											text = target.resolve_entity(text)
									value.append(text), "append attribute"
								attrib[key] = string.join(value, "")
				elif code == ENTITY:
					# text entities
					try:
						text = self.fixentity(text)
					except ValueError:
						text = target.resolve_entity(text)
					data(text)
				elif code == WHITESPACE:
					# whitespace
					pass
				else:
					# tag content <x>CONTENT</x>
					data(text)
		except EOFError:
			pass
		except SyntaxError, v:
			# generate nicer error message
			raise

	

	def fixentity(self, entity):
		# map entity name to built-in entity
		try:
			return self.xml_entities[entity]
		except KeyError:
			pass
		# assume numeric entity (raises ValueError if malformed)
		if entity[:2] == "#x":
			value = int(entity[2:], 16)
		else:
			value = int(entity[1:])
		if value > 127:
			return unichr(value)
		return chr(value)


class WeatherDotComCompiler:
	def __init__(self, xml_stream):
		self = self
		self.xml_stream = xml_stream

	""" Returns the content of a given tag in a stream as SEQUENCE like mySubSet=[]
		The value of the sequence will be an amount of DICTIONARYs like myDict={'DATA': 'Dortmund'} 
		
		Sample
		tmp = self.getSubSet(currentConditions[0], 't') // tag = 't' like for text
		occurence: 4 
		print tmp	 >> [[{'DATA': 'Partly'}, {'DATA': 'Cloudy'}], [{'DATA': 'W'}], [{'DATA': 'Low'}], [{'DATA': 'First'}, {'DATA': 'Quarter'}]] 
		print tmp[2] >> [{'DATA': 'Low'}]
	"""
	def getSubSet(self, stream, tag):
		occurCounter=0
		i=0
		startPos=[]
		endPos=[]
		#print i, startPos, endPos, tag
		#print "***\n", stream, "***\n"
		for item in stream:
			i+=1
			if item.has_key(tag):
				if item.get(tag) == 'START':
					occurCounter += 1
					startPos.append(i)
				if item.get(tag) == 'END':
					endPos.append(i-1)
		#print occurCounter, startPos, endPos
		i=0
		returnValue=[]
		while i < occurCounter:
			returnValue.append(stream.__getslice__(startPos[i],endPos[i]))
			i+=1;
		return returnValue
	
	"""
		getCurrentConditions 
	"""
	def getCurrentConditions(self, weatherItem):
		value = []
		i = 0
		
		units = ""
		units = self.getSubSet(self.xml_stream, 'head')
		currentLocation = ""
		currentLocation = self.getSubSet(self.xml_stream, 'loc')
		currentConditions = ""
		currentConditions = self.getSubSet(self.xml_stream, 'cc')
		#print occur, currentConditions
		
	
		weatherItem.caption = "Current"
		
		for item in (self.getSubSet(units[0], 'us'))[0]: # km/h mp/h
			#log(item.get('DATA'))
			weatherItem.unitSpeed = item.get('DATA')
		for item in (self.getSubSet(units[0], 'ut'))[0]: # F/C
			#log(item.get('DATA'))
			weatherItem.unitTemp = item.get('DATA')
		
		for item in (self.getSubSet(currentLocation[0], 'dnam'))[0]: #Selected City
			#log(item.get('DATA'))
			value.append(item.get('DATA'))
		weatherItem.location = string.join(value, " ")
		value = []
			
		for item in (self.getSubSet(currentConditions[0], 'lsup'))[0]: #Last Update
			if i > 2:
				break
			value.append(item.get('DATA'))
			i+=1
		weatherItem.date = string.join(value, " ")
		value = []; i=0
		
		for item in (self.getSubSet(currentConditions[0], 'obst'))[0]: # location
			#log(item.get('DATA'))
			pass
		for item in (self.getSubSet(currentConditions[0], 'tmp'))[0]: # temperature
			#log(item.get('DATA'))
			weatherItem.temp1 = item.get('DATA')
		for item in (self.getSubSet(currentConditions[0], 'flik'))[0]: # feels like
			#log(item.get('DATA'))
			weatherItem.temp2 = item.get('DATA')
		for item in (self.getSubSet(currentConditions[0], 't'))[0]: # text 1
			#log(item.get('DATA'))
			weatherItem.text = item.get('DATA')
		for item in (self.getSubSet(currentConditions[0], 'icon'))[0]: # icon 1
			#log(item.get('DATA'))
			weatherItem.icon = item.get('DATA')
		for item in (self.getSubSet(self.getSubSet(currentConditions[0], 'wind')[0], 's'))[0]: # wind - strength
			#log(item.get('DATA'))
			weatherItem.windStrength = item.get('DATA')
		for item in (self.getSubSet(self.getSubSet(currentConditions[0], 'wind')[0], 'd'))[0]: # wind
			#log(item.get('DATA'))
			pass
		for item in (self.getSubSet(self.getSubSet(currentConditions[0], 'wind')[0], 't'))[0]: # wind
			#log(item.get('DATA'))
			weatherItem.windFrom = item.get('DATA')
		for item in (self.getSubSet(currentConditions[0], 'hmid'))[0]: # humidity
			#log(item.get('DATA'))
			weatherItem.hmid = item.get('DATA')
		for item in (self.getSubSet(self.getSubSet(currentConditions[0], 'moon')[0], 'icon'))[0]: # moon
			#log(item.get('DATA'))
			pass
		for item in (self.getSubSet(self.getSubSet(currentConditions[0], 'moon')[0], 't'))[0]: # moon
			#log(item.get('DATA'))
			pass	
			
			
			
	"""
		getForcast 
	"""
	def getForecast(self, weatherItem):
		log("WeatherDotComCompiler::getForecast - item: %s" % weatherItem.id)
		forDay=weatherItem.id
		value = []
		i = 0
		
		
		units = ""
		units = self.getSubSet(self.xml_stream, 'head')
		forecast = ""
		forecast = self.getSubSet(self.xml_stream, 'dayf')
		day = ""
		day = (self.getSubSet(forecast[0], 'day'))[forDay]
		
		
		for item in (self.getSubSet(units[0], 'us'))[0]: # km/h mp/h
			#log(item.get('DATA'))
			weatherItem.unitSpeed = item.get('DATA')
		for item in (self.getSubSet(units[0], 'ut'))[0]: # F/C
			#log(item.get('DATA'))
			weatherItem.unitTemp = item.get('DATA')
			
	
		for item in day:
			if i > 0:
				break
			value = item.get('DATA')
			i+=1
		weatherItem.caption = value.get('t')
		weatherItem.date = value.get('dt')
		value = []; i=0
		
		
		for item in (self.getSubSet(day, 'hi'))[0]: # high-temp
			#log(item.get('DATA'))
			weatherItem.temp1 = item.get('DATA')
		for item in (self.getSubSet(day, 'low'))[0]: # low-temp
			#log(item.get('DATA'))
			weatherItem.temp2 = item.get('DATA')
		for item in (self.getSubSet(self.getSubSet(day, 'part')[0], 't'))[0]: # desc
			#log(item.get('DATA'))
			weatherItem.text = item.get('DATA')
		for item in (self.getSubSet(self.getSubSet(day, 'part')[0], 'icon'))[0]: # icon
			#log(item.get('DATA'))
			weatherItem.icon = item.get('DATA')

	


# Weather Item
class WeatherItem(PyFR.Utilities.ControllerUtilities):
	def __init__(self,item):
		self.id=item
		self.caption = "ERROR"
		self.unitTemp = " " #C/F
		self.unitSpeed = " " #kmh / mph
		self.temp1=" "	#high/current
		self.temp2=" "	#low/feels-like
		self.text="Something is wrong here"
		self.location=" "
		self.date=" " #lupdate/date
		self.icon="na"
		self.windFrom=" "
		self.windStrength=" "
		self.hmid=" "

	def GetName(self):
		return self.caption


	def GetPreviewImagePath(self):
		return ("/System/Library/CoreServices/Front Row.app/Contents/PlugIns/WeatherFront.frappliance/Contents/Resources/icons/%s.png" % self.icon)




class WeatherAPI(PyFR.Utilities.ControllerUtilities):
	def __init__(self):
		self = self
		self.internetConnection = 1
		self.rawXml = ''
		self.xmlScanner = ''
		self.xmlCompiler = ''
		self.locationCode = 'USCA0273'
		self.units = 'M'
		
	def DownloadWeather(self, location_id='0', units='M'):
		#url = "http://xoap.weather.com/weather/local/GMXX5312?cc=*&dayf=5&link=xoap&prod=xoap&par=1173899084&key=75c64d1ebc6ef7f7"
		url = "http://xoap.weather.com/weather/local/%s?cc=*&dayf=5&link=xoap&prod=xoap&par=1173899084&key=75c64d1ebc6ef7f7"
		url = url % (self.locationCode)
		
		
		if self.units == "M":
			url = url + '&unit=m'
		log(url)
		try:
			handler = urllib2.urlopen(url)
			self.rawXml = handler.read()
			handler.close()
			#log(self.rawXml)
			return 1
		except:
			log("WeatherAPI::DownloadWeather - no internet connection")
			self.internetConnection = 0
			return 0 #error
			
	def ReadConfigFile(self):
		infile = file('/System/Library/CoreServices/Front Row.app/Contents/PlugIns/WeatherFront.frappliance/Contents/Resources/mySettings.txt', 'r')
		for line in infile.readlines():
			#log(line)
			if string.find(line, 'LOCATIONCODE') != -1:
				value = string.split(line, '=')
				value[1] = string.rstrip(value[1], '\n')
				self.locationCode = value[1]
				log(self.locationCode)
			if string.find(line, 'UNITS') != -1:
				value = string.split(line, '=')
				value[1] = string.rstrip(value[1], '\n')
				self.units = value[1]
				log(self.units)
				
		infile.close()
		return
		

	def GetWeatherItems(self):
		log("WeatherAPI::GetWeathreItems")
		
		retval=[]
		
		if self.DownloadWeather() == 1:
			self.xmlScanner = XMLscanner()
			self.xmlScanner.scan(self.rawXml)
			self.xmlCompiler = WeatherDotComCompiler(self.xmlScanner.xml_stream)
			
			# current condition
			cc = WeatherItem('cc')
			self.xmlCompiler.getCurrentConditions(cc)
			retval.append(cc)
			
			# forecast
			fc0 = WeatherItem(0)
			self.xmlCompiler.getForecast(fc0)
			retval.append(fc0)
			
			fc1 = WeatherItem(1)
			self.xmlCompiler.getForecast(fc1)
			retval.append(fc1)
			
			fc2 = WeatherItem(2)
			self.xmlCompiler.getForecast(fc2)
			retval.append(fc2)
			
			fc3 = WeatherItem(3)
			self.xmlCompiler.getForecast(fc3)
			retval.append(fc3)
			

		else:
			error = WeatherItem("0")
			retval.append(error)

		return retval



WeatherAPI=WeatherAPI()
WeatherAPI.ReadConfigFile()

